package org.pixelgaffer.turnierserver.ailibrary;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import org.pixelgaffer.turnierserver.Logger;
import org.pixelgaffer.turnierserver.PropertyUtils;
/**
* @param <R>
* Die Antwort der Spiellogik
* @param <U>
* Die Antwort der Ki
*/
public abstract class Ai implements Runnable {
public static Logger logger = new Logger();
/**
* Die Connection zum Worker
*/
private Socket con;
/**
* Der Printwriter der Connection
*/
private BufferedOutputStream out;
/**
* Der BufferedReader der Connection
*/
private BufferedReader in;
/**
* Der kummulierte String von System.out
*/
protected StringBuilder output = new StringBuilder();
public Ai(String[] args) {
try {
PropertyUtils.loadProperties(args.length > 0 ? args[0] : "ai.prop");
logger.info("Connecting to " + PropertyUtils.getStringRequired(PropertyUtils.WORKER_HOST) + ":" + PropertyUtils.getIntRequired(PropertyUtils.WORKER_SERVER_PORT));
con = new Socket(PropertyUtils.getStringRequired(PropertyUtils.WORKER_HOST), PropertyUtils.getIntRequired(PropertyUtils.WORKER_SERVER_PORT));
out = new BufferedOutputStream(con.getOutputStream());
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
out.write((PropertyUtils.getStringRequired(PropertyUtils.WORKER_SERVER_AICHAR) + PropertyUtils.getStringRequired(PropertyUtils.AI_UUID) + "\n").getBytes(UTF_8));
out.flush();
boolean debug = PropertyUtils.getBoolean("turnierserver.debug", false);;
logger.info("Debugging is " + (debug ? "enabled" : "disabled"));
PrintStream stdout = System.out;
System.setOut(new PrintStream(new OutputStream() {
public void write(int b) throws IOException {
output.append((char) b);
if (debug)
stdout.write(b);
}
}));
PrintStream stderr = System.err;
System.setErr(new PrintStream(new OutputStream() {
public void write(int b) throws IOException {
output.append((char) b);
if (debug)
stderr.write(b);
}
}));
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Wird aufgerufen, sobald der Server ein Gamestate-Update sendet
*
* @param answer Die Antwort vom Server
*
* @return Die Antwort an den Server, null wenn keine gesendet werden soll
* (der Server wartet bei rundenbasierten Spielen trotzdem auf eine
* Antwort)
*/
protected abstract String update(String answer);
public final void run() {
try {
while (true) {
if (con.isClosed()) {
System.exit(0);
}
String line = in.readLine();
if (line == null) System.exit(0);
String response;
try {
response = update(line);
} catch(Exception e) {
crash(e);
continue;
}
response += ":" + output.toString().substring(0, Math.min(1000, output.length())).replace("\\", "\\\\").replace("\n", "\\n");
output.delete(0, output.length());
if (response != null) {
send(response);
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* ACHTUNG: Mit dieser Methode gibt die KI automatisch auf
*/
public final void surrender() {
send("SURRENDER");
}
/**
* ACHTUNG: Mit dieser Methode signalisiert man einen Crash -> Die KI verliert
*/
public final void crash(Throwable t) {
t.printStackTrace();
crash(t.getMessage() == null ? t.toString() : t.getMessage());
}
/**
* ACHTUNG: Mit dieser Methode signalisiert man einen Crash -> Die KI verliert
*/
public final void crash(String reason) {
try {
out.write(("CRASH " + reason + "\n").getBytes(UTF_8));
out.flush();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public final void send(String s) {
try {
out.write((s + "\n").getBytes(UTF_8));
out.flush();
} catch (Exception e) {
crash(e);
}
}
/**
* Muss in der Main-Methode aufgerufen werden, damit die KI sich zum Worker verbinden kann
*/
public final void start() {
new Thread(this).start();
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
crash(e);
}
}
}
}